package com.doodle.common.utils;

import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

import com.doodle.common.data.DataAdapter;
import com.doodle.common.drawable.AsyncDrawable;

public abstract class BaseMediaCursorAdapter extends CursorAdapter implements OnScrollListener, DataAdapter {

	private int mCacheSize = 32;
	
	protected SlidingWindow mSlidingWindow;
	private AsyncDrawableJobExecutor mJobExecutor;
	private AsyncDrawable mDrawables[];
	
	public BaseMediaCursorAdapter(Context context, Cursor c) {
		super(context, c, 0);
		mSlidingWindow = new SlidingWindow(this, mCacheSize);
		mJobExecutor = AsyncDrawableJobExecutor.getInstance();
		mDrawables = new AsyncDrawable[mCacheSize * 2];
	}
	
	public BaseMediaCursorAdapter(Context context, Cursor c, int cacheSize) {
		super(context, c, 0);
		mCacheSize = cacheSize;
		mSlidingWindow = new SlidingWindow(this, mCacheSize);
		mJobExecutor = AsyncDrawableJobExecutor.getInstance();
		mDrawables = new AsyncDrawable[mCacheSize * 2];
	}

	@Override
	public int size() {
		return getCount();
	}

	@Override
	public void prepare(int index) {
		int modIndex = index % mDrawables.length;
		AsyncDrawable drawable =  mDrawables[modIndex];
		if (drawable == null) {
			mDrawables[modIndex] = createDrawable(index, mJobExecutor);
		}
	}

	@Override
	public int start(int index) {
		AsyncDrawable drawable = mDrawables[index % mDrawables.length];
		if (drawable != null) {
			drawable.startLoad();
			return drawable.isRequestInProgress() ? 1 : 0;
		} else {
			Utils.log("Data start but not prepared: %d", index);
			return 0;
		}
	}

	@Override
	public void cancel(int index) {
		AsyncDrawable drawable = mDrawables[index % mDrawables.length];
		if (drawable != null) {
			drawable.cancelLoad();
		} else {
			Utils.log("Data cancel but not prepared: %d", index);
		}
	}

	@Override
	public void free(int index) {
		int modIndex = index % mDrawables.length;
		AsyncDrawable drawable = mDrawables[modIndex];
		if (drawable != null) {
			drawable.recycle();
		} else {
			Utils.log("Data free but not prepared: %d", index);
		}
		mDrawables[modIndex] = null;
	}

	@Override
	public void remove(int index) {
	}

	@Override
	public void onScroll(AbsListView list, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		if (list instanceof ListView) {
			firstVisibleItem -= ((ListView)list).getHeaderViewsCount();
		}
		int end = firstVisibleItem + visibleItemCount;
		end = Math.min(end, size());
		firstVisibleItem = Math.max(firstVisibleItem, 0);
		mSlidingWindow.setVisibleWindow(firstVisibleItem, end);
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		
	}
	
	@Override
	public Cursor swapCursor(Cursor c) {
		Cursor oldCursor = getCursor();
		if (oldCursor == null || oldCursor.isClosed()) {
			mSlidingWindow.clear();
			onCursorChanged(c);
			Cursor cursor = super.swapCursor(c);
			mSlidingWindow.onSizeChanged(size());
			return cursor;
		} else {
			mSlidingWindow.beforeDataChanged();
			onCursorChanged(c);
			Cursor cursor = super.swapCursor(c);
			mSlidingWindow.afterDataChanged(size());
			return cursor;
		}
	}
	
	protected void onCursorChanged(Cursor c) {
	}

	public AsyncDrawable getDrawable(int index) {
		int modIndex = index % mDrawables.length;
		AsyncDrawable drawable =  mDrawables[modIndex];
		if (drawable == null) {
			drawable = createDrawable(index, mJobExecutor);
			mDrawables[modIndex] = drawable;
		}
		return drawable;
	}

	@Override
	public long getDataId(int index) {
		return getItemId(index);
	}

	@Override
	public Object getDataObject(int index) {
		return mDrawables[index % mDrawables.length];
	}
	
	@Override
	public void setDataObject(int index, Object data) {
		mDrawables[index % mDrawables.length] = (AsyncDrawable)data;
	}
	
	@Override
	public void freeDataObject(Object data) {
		if (data != null) {
			((AsyncDrawable)data).recycle();
		}
	}
	
	@Override
	public long getItemId(int position) {
		if (mCursor == null || mCursor.isClosed()) {
			return 0;
		}
		return super.getItemId(position);
	}
	
	public void clearDrawables() {
		for (int i = 0; i < mDrawables.length; i++) {
			mDrawables[i] = null;
		}
	}

	protected abstract AsyncDrawable createDrawable(int index, AsyncDrawableJobExecutor jobExecutor);
}
